package iagovsothello.logger;

import iagovsothello.ai.*;
import iagovsothello.ai.heuristics.HeuristicFunctionCorners;
import iagovsothello.ai.heuristics.HeuristicFunctionEdge;
import iagovsothello.ai.heuristics.HeuristicFunctionMobility;
import iagovsothello.ai.heuristics.HeuristicFunctionScore;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

public class ClashExperiment extends ExperimentContainer {

	private static final long serialVersionUID = 3018753660989235635L;
	private Agent subject;
	private int rounds;
	private float rating;
	
	public ClashExperiment(String name, int dimension, int rounds, Agent subject, List<Agent> opponents, boolean autoMatch) 
	{
		super(name, dimension);
		this.subject = subject;
		this.rounds = rounds;
		for (Agent o : opponents)
		{
			if (subject.equals(o))
			{
				if (autoMatch)
					add(subject+" VS "+o,rounds,subject,o);
			}
			else
			{
				add(subject+" VS "+o,rounds,subject,o);
				add(o+" VS "+subject,rounds,o,subject);
			}
		}
	}

	public ClashExperiment(String name, boolean print, boolean basicPrint, int dimension, int rounds, Agent subject, List<Agent> opponents, boolean autoMatch) 
	{
		super(name, print, basicPrint, dimension);
		this.subject = subject;
		this.rounds = rounds;
		for (Agent o : opponents)
		{
			if (subject.equals(o))
			{
				if (autoMatch)
					add(subject+" VS "+o,rounds,subject,o);
			}
			else
			{
				add(subject+" VS "+o,rounds,subject,o);
				add(o+" VS "+subject,rounds,o,subject);
			}
		}
	}
	
	public static void main(String[] args) 
	{
		LinkedList<Agent> opponents = new LinkedList<Agent>();
		opponents.add(new Randomizer(new Random()));
		opponents.add(new Greedy());
	//	opponents.add(new Minimax());
	//	opponents.add(new ABPruning());
		opponents.add(new HMinimax(5,new HeuristicFunctionScore()));
		opponents.add(new HMinimax(5,new HeuristicFunctionMobility()));
		opponents.add(new HMinimax(5,new HeuristicFunctionCorners()));
		opponents.add(new HABPruning(5,new HeuristicFunctionScore()));
		opponents.add(new HABPruning(5,new HeuristicFunctionMobility()));
		opponents.add(new HABPruning(5,new HeuristicFunctionCorners()));


		//TreeSearchAgent subject = new Minimax();
		TreeSearchAgent subject = new HABPruning(5, new HeuristicFunctionEdge());
	//	TreeSearchAgent subject = new HABPruning(8, new HeuristicFunctionMobility());
	//	subject.setExpectedSequencingActive(true);
	//  subject.setHeuristicOpeningActive(true);
		
		ClashExperiment exp = new ClashExperiment("TEST",8,10,subject,opponents,false);
		exp.setThreads(2);
		System.out.println("SIZE: "+exp.list.size());
		exp.start();
	}
	
	public int getTotalSubjectVictories()
	{
		int victories = 0;
		for (BasicExperiment ex : list)
		{
			victories += ex.getVictories(subject);
		}
		return victories;
	}
	
	public int getPlayer1SubjectVictories()
	{
		int victories = 0;
		for (BasicExperiment ex : list)
		{
			if (ex.isPlayer(subject) == 1)
				victories += ex.getVictories(subject);
		}
		return victories;
	}
	
	public int getPlayer2SubjectVictories()
	{
		int victories = 0;
		for (BasicExperiment ex : list)
		{
			if (ex.isPlayer(subject) == 2)
				victories += ex.getVictories(subject);
		}
		return victories;
	}
	
	public long[] getTotalSubjectMeanTimes()
	{
		return getMeanTimes(subject);
	}
	
	public long[] getPlayer1SubjectMeanTimes()
	{
		int N = (dimension*dimension-4)/2 + (dimension*dimension-4)%2;
		
		long[] times = new long[N];
		Arrays.fill(times, 0);
		int number = 0;
		
		for (BasicExperiment ex : list)
		{
			if (ex.isPlayer(subject) == 1)
			{
				long[] exTimes = ex.getMeanTimes(1);
				for (int j = 0; j < exTimes.length; j++)
				{
					times[j] += exTimes[j];
				}
				number++;
			}
		}
		
		for (int j = 0; j < N; j++)
		{
			times[j] = times[j]/number;
		}
		return times;
	}
	
	public long[] getPlayer2SubjectMeanTimes()
	{
		int N = (dimension*dimension-4)/2 + (dimension*dimension-4)%2;
		
		long[] times = new long[N];
		Arrays.fill(times, 0);
		int number = 0;
		
		for (BasicExperiment ex : list)
		{
			if (ex.isPlayer(subject) == 2)
			{
				long[] exTimes = ex.getMeanTimes(2);
				for (int j = 0; j < exTimes.length; j++)
				{
					times[j] += exTimes[j];
				}
				number++;
			}
		}
		
		for (int j = 0; j < N; j++)
		{
			times[j] = times[j]/number;
		}
		return times;
	}
	
	public long getTotalSubjectMaxTime()
	{
		return getMaxTime(subject);
	}
	
	public long getPlayer1SubjectMaxTime()
	{
		long maxTime = Long.MIN_VALUE;
		for (BasicExperiment ex : list)
		{
			if (ex.isPlayer(subject) == 1)
			{
				long exMaxTime = ex.getMaxTime(1);
				if (exMaxTime > maxTime)
				{
					maxTime = exMaxTime;
				}
			}
		}
		return maxTime;
	}
	
	public long getPlayer2SubjectMaxTime()
	{
		long maxTime = Long.MIN_VALUE;
		for (BasicExperiment ex : list)
		{
			if (ex.isPlayer(subject) == 2)
			{
				long exMaxTime = ex.getMaxTime(2);
				if (exMaxTime > maxTime)
				{
					maxTime = exMaxTime;
				}
			}
		}
		return maxTime;
	}
	
	public long[] getTotalSubjectVisitedNodes()
	{
		return getVisitedNodes(subject);
	}
	
	public long[] getPlayer1SubjectVisitedNodes()
	{
		int N = (dimension*dimension-4)/2 + (dimension*dimension-4)%2;
		
		long[] nodes = new long[N];
		Arrays.fill(nodes, 0);
		int number = 0;
		
		for (BasicExperiment ex : list)
		{
			if (ex.isPlayer(subject) == 1)
			{
				long[] exNodes = ex.getVisitedNodes(1);
				for (int j = 0; j < exNodes.length; j++)
				{
					nodes[j] += exNodes[j];
				}
				number++;
			}
		}
		
		for (int j = 0; j < N; j++)
		{
			nodes[j] = nodes[j]/number;
		}
		return nodes;
	}
	
	public long[] getPlayer2SubjectVisitedNodes()
	{
		int N = (dimension*dimension-4)/2 + (dimension*dimension-4)%2;
		
		long[] nodes = new long[N];
		Arrays.fill(nodes, 0);
		int number = 0;
		
		for (BasicExperiment ex : list)
		{
			if (ex.isPlayer(subject) == 2)
			{
				long[] exNodes = ex.getVisitedNodes(2);
				for (int j = 0; j < exNodes.length; j++)
				{
					nodes[j] += exNodes[j];
				}
				number++;
			}
		}
		
		for (int j = 0; j < N; j++)
		{
			nodes[j] = nodes[j]/number;
		}
		return nodes;
	}
	
	public float getRating()
	{
		return rating;
	}
	
	protected void end()
	{
		//niente
	}
	
	protected void print() 
	{
		int player1Victories = getPlayer1SubjectVictories();
		int player2Victories = getPlayer2SubjectVictories();
		int totalVictories = player1Victories + player2Victories;
		
		long[] meansTotal = getTotalSubjectMeanTimes();
		long[] meansPlayer1 = getPlayer1SubjectMeanTimes();
		long[] meansPlayer2 = getPlayer2SubjectMeanTimes();
		
		long[] nodesPlayer1 = getPlayer1SubjectVisitedNodes();
		long[] nodesPlayer2 = getPlayer2SubjectVisitedNodes();
		
		long mean = getMeanTime(meansTotal);
		long maxTimeTotal = getTotalSubjectMaxTime();
		long maxTimePlayer1 = getPlayer1SubjectMaxTime();
		long maxTimePlayer2 = getPlayer2SubjectMaxTime();
		
		rating = getRating(player1Victories,mean,meansPlayer2[0],meansPlayer1[0]);
				
		System.out.println("\n\n--------FINE ESPERIMENTO: "+getName()+"--------");
		System.out.println("soggetto: "+subject);
		System.out.println("rounds per ciascuno scontro: "+rounds);
		System.out.println("numero totale di partite: "+(rounds*list.size()));
		
		System.out.println("vittorie totali: "+totalVictories);
		System.out.println("vittorie come giocatore bianco: "+player1Victories);
		System.out.println("vittorie come giocatore nero: "+player2Victories);
		
		System.out.println("tempo medio totale per il calcolo di una mossa: "+mean+" us");
		System.out.println("tempo medio come giocatore bianco per il calcolo di una mossa: "+getMeanTime(meansPlayer1)+ " us");
		System.out.println("tempo medio come giocatore nero per il calcolo di una mossa: "+getMeanTime(meansPlayer2) + " us");
		
		System.out.println("tempo massimo totale per il calcolo di una mossa: "+maxTimeTotal+" us");
		System.out.println("tempo massimo come giocatore bianco per il calcolo di una mossa: "+maxTimePlayer1+" us");
		System.out.println("tempo massimo come giocatore nero per il calcolo di una mossa: "+maxTimePlayer2+" us");
		
		System.out.printf("\nrating: %.2f\n",rating);
		
		System.out.println("\ntempi medi di calcolo per ciascun turno e nodi visitati mediamente:");
//		System.out.println("Iterazione\tTOT\tTurno\tBianco\tTurno\tNero");
		System.out.println("Turno\tTempo (us)\tNodi");
		for (int i = 0, j = 1; i < meansTotal.length; i++, j += 2)
		{
//			System.out.println((i+1)+"\t"+meansTotal[i]+"\t"+((i+1)*2)+"\t"+meansPlayer1[i]+"\t"+((i+1)*2-1)+"\t"+meansPlayer2[i]);
			System.out.println((j)+"\t"+meansPlayer2[i]+"\t"+nodesPlayer2[i]);
			System.out.println((j+1)+"\t"+meansPlayer1[i]+"\t"+nodesPlayer1[i]);
		}
		System.out.println("\n\n");
	}
}
